package com.compress;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.List;

import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;

import com.compress.util.FileUtil;
import com.compress.util.HtmlUtil;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;

public class JSCompressor implements Compressor, Mergeable {
	public JSCompressor() {

	}

	@Override
	public void compress(FilePath path) throws Exception {
		Assert.assertNotNull(path, "path不能为空");
		Assert.assertNotEmpty(path.getParentDir(), "parentDir不能为空");
		Assert.assertNotEmpty(path.getOutDir(), "outDir不能为空");
		Assert.assertNotEqual(path.getParentDir(), path.getOutDir(), "parentDir和outDir不能为同一目录");
		String parentDir = path.getParentDir();
		File rootFile = new File(parentDir);
		Assert.assertFileExist(rootFile, "parentDir指定文件或目录不存在");
		if (rootFile.isFile()) {
			if (!path.getParentDir().endsWith(".js") || !!path.getParentDir().endsWith(".JS")) {
				throw new IllegalArgumentException(parentDir + "不是有效的js文件");
			}
			this.compressOneFile(path.getOutDir(), rootFile);
		} else {
			this.compressDir(path.getOutDir(), rootFile);
		}
	}

	@Override
	public void merge(MergeFile file) throws Exception {
		Assert.assertNotNull(file, "file can not be null");
		Assert.assertNotEmpty(file.getIndexFilePath(), "indexFilePath不能为空");
		Assert.assertNotEmpty(file.getOutDir(), "outDir不能为空");
		File indexFile = new File(file.getIndexFilePath());
		Assert.assertFileExist(indexFile, file.getIndexFilePath() + "文件不存在或不是一个有效的js文件");

		// 1.获取需要合并的文件路径
		String html = FileUtil.copy(file.getIndexFilePath());
		List<String> list = HtmlUtil.matchAll(html, "script", "src");
		if (list == null || list.size() == 0) {
			System.out.println("没有找到需要合并的文件");
			return;
		}

		for (String path : file.getExcludes()) {
			if (list.contains(path)) {
				list.remove(path);
			}
		}
		System.out.println("需要合并的文件列表：");
		for (String path : list) {
			System.out.println(path);
		}

		// 2.将所有的文件内容合并
		System.out.println("开始合并文件...");
		StringBuffer sb = new StringBuffer();
		for (String filepath : list) {
			File f = new File(indexFile.getParent(), filepath);
			String str = FileUtil.copy(f.getAbsolutePath());
			sb.append(str+";");
		}

		// 3.压缩并生成文件（data.all.min.js）
		System.out.println("开始压缩文件...");
		this.compressString(file.getOutDir(), sb.toString(), "data.all.min.js");
		System.out.println("操作完成！");
	}

	/**
	 * 对目录的脚本文件进行压缩
	 * 
	 * @param outPath
	 * @param parentFile
	 * @throws Exception
	 */
	private void compressDir(String outPath, File parentFile) throws Exception {
		File[] files = parentFile.listFiles(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {
				File file = new File(dir, name);
				return file.isDirectory() || name.toLowerCase().endsWith(".js");
			}
		});
		for (File file : files) {
			if (file.isFile()) {
				this.compressOneFile(outPath, file);
			} else {
				compressDir(outPath + File.separator + file.getName(), file);
			}
		}
	}

	/**
	 * 对单个文件进行压缩
	 * 
	 * @param outPath
	 * @param sourceFile
	 * @throws Exception
	 */
	private void compressOneFile(String outPath, File sourceFile) throws Exception {
		String content = FileUtil.copy(sourceFile.getAbsolutePath());
		this.compressString(outPath, content, sourceFile.getName());

	}

	/**
	 * 对字符串进行压缩
	 * 
	 * @param outPath
	 *            输出路径（目录或文件）
	 * @param content
	 *            源字符串
	 * @param fileName
	 *            生成的文件名
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private void compressString(String outPath, String content, String fileName)
			throws FileNotFoundException, IOException {
		Reader in = new StringReader(content);
		/*FileWriter writer = new FileWriter("g://a.js");
		writer.write(content);
		if(true){
			return;
		}*/
		JavaScriptCompressor jsCompressor = new JavaScriptCompressor(in, new ErrorReporter() {

			@Override
			public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) {
				if (line < 0) {
					System.err.println("/n[WARNING] " + message);
				} else {
					System.err.println("/n[WARNING] " + line + ':' + lineOffset + ':' + message);
				}
			}

			@Override
			public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource,
					int lineOffset) {
				error(message, sourceName, line, lineSource, lineOffset);
				return new EvaluatorException(message);
			}

			@Override
			public void error(String message, String sourceName, int line, String lineSource, int lineOffset) {
				// TODO Auto-generated method stub
				if (line < 0) {
					System.err.println("/n[ERROR] " + message);
				} else {
					System.err.println("/n[ERROR] " + line + ':' + lineOffset + ':' + message);
				}
			}
		});
		Writer out = null;
		File outFile = new File(outPath);
		if (!outFile.exists()) {
			outFile.mkdirs();
		}
		if (outFile.isDirectory()) {
			outFile = new File(outFile, fileName);
		}
		out = new FileWriter(outFile);
		jsCompressor.compress(out, -1, true, false, false, false);

		in.close();
		out.flush();
		out.close();

	}

}
